package pl.wroc.pwr.docs.repos.dao.impl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.TermFreqVector;
import org.apache.lucene.search.Query;
import org.hibernate.search.ProjectionConstants;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.FullTextQuery;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.hibernate.search.query.facet.Facet;
import org.hibernate.search.query.facet.FacetSelection;
import org.hibernate.search.query.facet.FacetSortOrder;
import org.hibernate.search.query.facet.FacetingRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import pl.wroc.pwr.docs.repos.dao.SearchDocumentDao;
import pl.wroc.pwr.docs.repos.domain.Document;


@Repository("documentDaoImpl")
@Transactional
public class LuceneSearchDocumentDaoImpl implements SearchDocumentDao {
	

	@Autowired
	private FullTextEntityManager fullTextEntityManager;
	
	private QueryBuilder queryBuilder(){
		return fullTextEntityManager.getSearchFactory()
		                            .buildQueryBuilder()
		                            .forEntity(Document.class)
		                            .get();
	}
	
	private int getLuceneId(int id){
		Query query = queryBuilder().keyword()
		                            .onField("id")
		                            .matching(id)
		                            .createQuery();
		
		return (int)(Integer) ((Object[]) fullTextEntityManager.createFullTextQuery(query, Document.class)
		                                           .setProjection(ProjectionConstants.DOCUMENT_ID)
		                                           .getSingleResult())[0];
	}
	
	@Override
	public FullTextQuery buildQuery(String content){
		Query query;
		FullTextQuery fullTextQuery;
		
		if(StringUtils.isNotBlank(content)){
			query = queryBuilder().keyword()
		                            .onFields("indexedContent")
		                            .matching(content)
		                            .createQuery();
		}
		else
			query = queryBuilder().all().createQuery();
		 
		FacetingRequest formatFacetingRequest = queryBuilder().facet()
															 .name( "formatFaceting" )
			    									         .onField( "format")
			    									         .discrete()
			    									         .orderedBy(FacetSortOrder.FIELD_VALUE )
			    									         .includeZeroCounts(false)
			    									         .createFacetingRequest();
		
		fullTextQuery = fullTextEntityManager.createFullTextQuery(query, Document.class);
		fullTextQuery.getFacetManager().enableFaceting(formatFacetingRequest);

		return fullTextQuery;
	}
	
	public FullTextQuery filtrQuery (FullTextQuery fullTextQuery, List<String> formats) {
		
		List<Facet> facet = getFacets(fullTextQuery);
		List<Facet> toApply = new ArrayList<Facet>();
		
		for(String format: formats){
			for(Facet fa : facet){
				if(fa.getValue().equals(format)){
					toApply.add(fa);
					break;
				}
			}
		}
		
		applyFacets(fullTextQuery, toApply);
		
		return fullTextQuery;
	}
	
	public FullTextQuery resetQuery (FullTextQuery fullTextQuery) {
		disableFacets(fullTextQuery);
		return fullTextQuery;
	}

	@Override
	public Page<Document> search(Pageable pageable, FullTextQuery fullTextQuery) {
		int total;
		fullTextQuery.setFirstResult(pageable.getOffset());
		fullTextQuery.setMaxResults(pageable.getPageSize());
		
		@SuppressWarnings("unchecked")
		List<Document> result = (List<Document>) fullTextQuery.getResultList();
		total = fullTextQuery.getResultSize();
		return new PageImpl<Document>(result, pageable, total);
	}
	
	@Override
	public List<Facet> getFacets(FullTextQuery fullTextQuery) {
		List<Facet> facets = fullTextQuery.getFacetManager().getFacets( "formatFaceting" );

		return facets;
	}
	
	@Override
	public void applyFacets(FullTextQuery fullTextQuery, List<Facet> facets){
		FacetSelection selection = fullTextQuery.getFacetManager()
		                                        .getFacetGroup( "formatFaceting" );
		for(Facet f: facets){
			selection.selectFacets(f);
		}
	}
	
	@Override
	public void disableFacets(FullTextQuery fullTextQuery){
		fullTextQuery.getFacetManager().getFacetGroup( "formatFaceting" ).clearSelectedFacets();
	}
	
	@Override
	public TermFreqVector getTermFreqVector(int id) throws IOException {
		IndexReader reader = fullTextEntityManager.getSearchFactory()
		                                          .getIndexReaderAccessor()
		                                          .open(Document.class);
		try {
			int luceneId = getLuceneId(id);
			return reader.getTermFreqVector(luceneId, "indexedContent");
		}
		finally {
			fullTextEntityManager.getSearchFactory()
			                     .getIndexReaderAccessor()
			                     .close(reader);
		}
	}
}
